﻿// MutaRaid by fiftypence
//
// SVN: http://fiftypence.googlecode.com/svn/trunk/
// 
// As always, if you wish to reuse code, please seek permission first 
// and provide credit where appropriate

using System;
using System.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Drawing;

using Styx;
using Styx.Combat.CombatRoutine;
using Styx.Helpers;
using Styx.Logic;
using Styx.Logic.Combat;
using Styx.Logic.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;

using Doctrine.Talents;

using CommonBehaviors.Actions;
using TreeSharp;
using Action = TreeSharp.Action;

namespace MutaRaidBT
{
    public partial class MutaRaidBT : CombatRoutine
    {
        #region Variables

        private int mCurrentEnergy = 0;
        private WoWPlayer mFocusTarget = null;

        #endregion

        #region Helpers

        //******************************************************************************************
        //* The following helpers may be reused at will, as long as credit is given to fiftypence. *
        //******************************************************************************************

        #region Non-BT Helpers

        /// <summary>
        ///     List of nearby enemy units that pass certain criteria, this list should only return units 
        ///     in active combat with the player, the player's party, or the player's raid.
        ///     (NO LUA)
        /// </summary>

        private List<WoWUnit> EnemyUnits
        {
            get
            {
                return
                    ObjectManager.GetObjectsOfType<WoWUnit>(true, false)
                    .Where(unit =>
                        !unit.IsFriendly
                        && (unit.IsTargetingMeOrPet
                           || unit.IsTargetingMyPartyMember
                           || unit.IsTargetingMyRaidMember
                           || unit.IsPlayer)
                        && !unit.IsNonCombatPet
                        && !unit.IsCritter
                        && unit.Distance2D
                     <= 12).ToList();
            }
        }

        /// <summary>
        ///     Updates current energy resource of the player. This is used
        ///     to fix the slow updating of ObjectManager.Me.CurrentEnergy.
        ///     (USES LUA)
        /// </summary>

        private void UpdateEnergy()
        {
            mCurrentEnergy = Lua.GetReturnVal<int>("return UnitMana(\"player\");", 0);
        }

        /// <summary>
        ///     Tracks and sets focus using Me.FocusedUnit.
        ///     Access the focused unit by using mFocusTarget.
        ///     (NO LUA)
        /// </summary>

        private void UpdateFocus()
        {
            WoWPlayer curFocus = (WoWPlayer) Me.FocusedUnit;

            if (curFocus == null)
            {
                mFocusTarget = null;
            }
            else
            {
                if (IsFocusValidTarget())
                {
                    if (mFocusTarget != curFocus)
                    {
                        mFocusTarget = curFocus;

                        Logging.Write(Color.Orange, "Setting " + mFocusTarget.Name + " as focus target.");
                    }
                }
                else
                {
                    if (mFocusTarget != null)
                    {
                        Logging.Write(Color.Orange, "Our focus target is dead or out of range. Clearing focus target.");

                        mFocusTarget = null;
                    }
                }


            }
        }

        /// <summary>
        ///     Returns true if the focus target is valid, in range, and not dead.
        ///     (NO LUA)
        /// </summary>
        /// <returns></returns>


        private bool IsFocusValidTarget()
        {
            WoWPlayer curFocus = (WoWPlayer) Me.FocusedUnit;

            return (curFocus.Distance2D < 100 && curFocus.InLineOfSight && curFocus.IsAlive && curFocus.IsInMyPartyOrRaid);
        }


        /// <summary>
        ///     Returns true if player is behind current the target.
        ///     (NO LUA)
        /// </summary>
        /// <returns></returns>

        private bool IsBehindTarget()
        {
            if (Me.CurrentTarget.MeIsBehind)
                return true;
            else return false;
        }

        /// <summary>
        ///     Checks to see if specified target is under the effect of crowd control
        ///     (NO LUA)
        /// </summary>
        /// <param name="target">Target</param>
        /// <returns></returns>

        private bool IsCrowdControlled(WoWUnit target)
        {
            // Just want to throw a shout-out to Singular for this function.
            return target.GetAllAuras().Any(
            unit => unit.Spell.Mechanic == WoWSpellMechanic.Banished
                || unit.Spell.Mechanic == WoWSpellMechanic.Charmed
                || unit.Spell.Mechanic == WoWSpellMechanic.Horrified
                || unit.Spell.Mechanic == WoWSpellMechanic.Incapacitated
                || unit.Spell.Mechanic == WoWSpellMechanic.Polymorphed
                || unit.Spell.Mechanic == WoWSpellMechanic.Sapped
                || unit.Spell.Mechanic == WoWSpellMechanic.Shackled
                || unit.Spell.Mechanic == WoWSpellMechanic.Asleep
                || unit.Spell.Mechanic == WoWSpellMechanic.Frozen
            );
        }

        /// <summary>
        ///     Checks to see if any nearby units are under breakable crowd control.
        ///     (NO LUA)
        /// </summary>

        private bool ShouldWeAoe()
        {
            foreach (WoWUnit unit in EnemyUnits)
            {
                if (IsCrowdControlled(unit))
                {
                    return false;
                }
            }

            return true;
        }


        /// <summary>
        ///     Checks is specified unit has active aura. 
        ///     Returns true if yes, false if no.
        ///     (NO LUA)
        /// </summary>
        /// <param name="unitName"></param>
        /// <param name="auraName"></param>
        /// <returns></returns>

        private bool UnitHasAura(WoWUnit unitName, string auraName)
        {
            WoWAuraCollection unitAuras;

            unitAuras = unitName.GetAllAuras();

            foreach (WoWAura aura in unitAuras)
            {
                if (aura.Name == auraName)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        ///     Check if specified unit has active aura created by specified GUID. 
        ///     Returns true if yes, false if no.
        ///     (NO LUA)
        /// <param name="unitName"></param>
        /// <param name="creatorGUID"></param>
        /// <param name="auraName"></param>
        /// <returns></returns>

        private bool UnitHasAura(WoWUnit unitName, UInt64 creatorGUID, string auraName)
        {
            WoWAuraCollection unitAuras;

            unitAuras = unitName.GetAllAuras();

            foreach (WoWAura aura in unitAuras)
            {
                if (aura.Name == auraName && aura.CreatorGuid == creatorGUID)
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        ///     Returns the time left on specified aura of specified unit. 
        ///     (NO LUA)
        /// </summary>
        /// <param name="unitName"></param>
        /// <param name="auraName"></param>
        /// <returns></returns>

        private double AuraTimeLeft(WoWUnit unitName, string auraName)
        {
            WoWAuraCollection unitAuras;

            unitAuras = unitName.GetAllAuras();

            foreach (WoWAura aura in unitAuras)
            {
                if (aura.Name == auraName)
                {
                    return aura.TimeLeft.Seconds;
                }
            }

            return 0;
        }

        /// <summary>
        ///     Returns the time left on specified aura created by specified guid, on specified unit. 
        ///     (NO LUA)
        /// </summary>
        /// <param name="unitName"></param>
        /// <param name="auraName"></param>
        /// <returns></returns>

        private double AuraTimeLeft(WoWUnit unitName, UInt64 creatorGUID, string auraName)
        {
            WoWAuraCollection unitAuras;

            unitAuras = unitName.GetAllAuras();

            foreach (WoWAura aura in unitAuras)
            {
                if (aura.Name == auraName && aura.CreatorGuid == creatorGUID)
                {
                    return aura.TimeLeft.Seconds;
                }
            }

            return 0;
        }

        /// <summary>
        ///     Gets the cooldown of specified spell. (NO LUA)
        /// </summary>
        /// <param name="SpellName">Specified Spell</param>
        /// <returns>Spell cooldown</returns>

        public double SpellCooldown(string SpellName)
        {
            return SpellManager.Spells[SpellName].CooldownTimeLeft.TotalSeconds;
        }

        /// <summary>
        ///     Check if player's target is a boss using Lua.
        ///     (USES LUA)
        /// </summary>

        private bool IsTargetBoss()
        {
            string UnitClassification = Lua.GetReturnValues("local classification = UnitClassification(\"target\"); return classification")[0];

            if (!Me.IsInRaid)
            {
                if (UnitClassification == "worldboss" ||
                   (Me.CurrentTarget.Level == 87 && Me.CurrentTarget.Elite) ||
                   (Me.CurrentTarget.Level == 88))
                    return true;

                else return false;
            }
            else
            {
                if (UnitClassification == "worldboss" ||
                   (Me.CurrentTarget.Level == 88 && Me.CurrentTarget.Elite))
                    return true;

                else return false;
            }
        }

        /// <summary>
        ///     Returns true if a spell is not on cooldown or if
        ///     there is 0.3 or less left on GCD, AND the spell is known.
        ///     (NO LUA)
        /// </summary>

        private bool CanCastSpell(string SpellName)
        {
            return (SpellCooldown(SpellName) <= 0.3 && SpellManager.HasSpell(SpellName));
        }

        #endregion

        #region BT Helpers

        #region Cast Spell

        /// <summary>
        ///     Checks if specified spell is castable, if so casts it and writes to log.
        ///     (NO LUA)
        /// </summary>
        /// <param name="SpellName">Spell name</param>
        /// <returns></returns>

        private Composite CastSpell(string SpellName)
        {
            return new Decorator(ret => Me.CurrentTarget != null && CanCastSpell(SpellName),
                new Action(delegate
                    {
                        SpellManager.Cast(SpellName);
                        Logging.Write(Color.LightBlue, "[" + mCurrentEnergy + "] [" + Me.ComboPoints + "] " + SpellName);
                    }
                )
            );
        }

        /// <summary>
        ///     Checks if specified spell is castable, if so casts it and writes to log.
        ///     Uses specified conditions.
        ///     (NO LUA)
        /// </summary>
        /// <param name="SpellName">Spell name</param>
        /// <param name="Conditions">Specified conditions</param>
        /// <returns></returns>

        private Composite CastSpell(string SpellName, CanRunDecoratorDelegate Conditions)
        {
            return new Decorator(ret => Conditions(ret) && CanCastSpell(SpellName),
                new Action(delegate
                    {
                        SpellManager.Cast(SpellName);
                        Logging.Write(Color.LightBlue, "[" + mCurrentEnergy + "] [" + Me.ComboPoints + "] " + SpellName);
                    }
                )
            );
        }

        /// <summary>
        ///     Checks if specified spell is castable, if so casts it and writes to log.
        ///     Casts on focus
        ///     (NO LUA)
        /// </summary>
        /// <param name="SpellName">Spell name</param>
        /// <returns></returns>

        #endregion

        #region Cast Focus

        private Composite CastSpellOnFocus(string SpellName)
        {
            return new Decorator(ret => mFocusTarget != null && SpellManager.CanCast(SpellName, Me.FocusedUnit),
                new Action(delegate
                    {
                        SpellManager.Cast(SpellName, Me.FocusedUnit);
                        Logging.Write(Color.Yellow, SpellName);
                    }
                )
            );
        }

        /// <summary>
        ///     Checks if specified spell is castable, if so casts it and writes to log.
        ///     Casts on focus, uses specified conditions
        ///     (NO LUA)
        /// </summary>
        /// <param name="SpellName">Spell name</param>
        /// <param name="Conditions">Specified conditions</param>
        /// <returns></returns>

        private Composite CastSpellOnFocus(string SpellName, CanRunDecoratorDelegate Conditions)
        {
            return new Decorator(ret => Conditions(ret) && mFocusTarget != null && SpellManager.CanCast(SpellName, Me.FocusedUnit),
                new Action(delegate
                    {
                        SpellManager.Cast(SpellName, Me.FocusedUnit);
                        Logging.Write(Color.Yellow, SpellName);
                    }
                )
            );
        }

        #endregion

        #region Cast Cooldowns

        /// <summary>
        ///     Checks if specified cooldown is castable, if so casts it and writes to log.
        ///     Casts with Lua to make use of the ability queue.
        ///     (NO LUA)
        /// </summary>
        /// <param name="SpellName">Cooldown name</param>
        /// <returns></returns>

        private Composite CastCooldown(string CooldownName)
        {
            return new Decorator(ret => CanCastSpell(CooldownName),
                new Action(delegate
                    {
                        SpellManager.Cast(CooldownName);
                        Logging.Write(Color.Yellow, CooldownName);
                    }
                )
            );
        }

        /// <summary>
        ///     Checks if specified cooldown is castable, if so casts it and writes to log.
        ///     Uses specified conditions.
        ///     Casts with Lua to make use of the ability queue.
        ///     (NO LUA)
        /// </summary>
        /// <param name="SpellName">Cooldown name</param>
        /// <param name="Conditions">Specified conditions</param>
        /// <returns></returns>

        private Composite CastCooldown(string CooldownName, CanRunDecoratorDelegate Conditions)
        {
            return new Decorator(ret => Conditions(ret) && CanCastSpell(CooldownName),
                new Action(delegate
                    {
                        SpellManager.Cast(CooldownName);
                        Logging.Write(Color.Yellow, CooldownName);
                    }
                )
            );
        }

        #endregion

        #region Movement

        private Composite MoveToTarget(WoWUnit target, int range)
        {
            return new PrioritySelector(
                new Decorator(ret => target.Distance > range,
                    new Action(delegate
                        {
                            Navigator.MoveTo(target.Location);
                        }
                    )
                ),

                new Decorator(ret => target.Distance < range && Me.IsMoving,
                    new Action(delegate
                        {
                            Navigator.PlayerMover.MoveStop();
                        }
                    )
                )    
            );
        }

        private Composite FaceTarget(WoWUnit target)
        {
            return new Decorator(ret => !Me.IsSafelyBehind(target),
                new Action(delegate
                    {
                        target.Face();
                        Logging.Write(Color.Orange, "Facing" + target.Name);
                    }
                )
            );
        }

        #endregion

        /// <summary>
        ///     Checks if player is auto-attacking, and if not toggles auto-attack.
        ///     (NO LUA)
        /// </summary>
        /// <returns></returns>

        private Composite AutoAttack()
        {
            return new Decorator(ret => !Me.IsAutoAttacking,
                new Action(delegate
                    {
                        Me.ToggleAttack();
                        Logging.Write(Color.Orange, "Auto-attack");
                    }
                )
            );
        }

        /// <summary>
        ///     Checks if player is auto-attacking, and if not toggles auto-attack.
        ///     Uses specified conditions.
        ///     (NO LUA)
        /// </summary>
        /// <returns></returns>

        private Composite AutoAttack(CanRunDecoratorDelegate Conditions)
        {
            return new Decorator(ret => Conditions(ret) && !Me.IsAutoAttacking,
                new Action(delegate
                    {
                        Me.ToggleAttack();
                        Logging.Write(Color.Orange, "Auto-attack");
                    }
                )
            );
        }

        #endregion

        #endregion
    }
}
